Perjashtimet dhe trajtimi i tyre

Gjate shpjegimeve te deritanishme, here pas here kemi permendur faktin qe PHP-ja mund te gjeneroje gabime ne prani te situatave te ndryshme. Imagjinoni sikur nje funksion te caktuar i cili pranon nje argument te tipit array ne hyrje, ne i kalojme nje argument te tipit string. Cfare do te ndodhe ne kete moment? Normalisht, deri tani jemi mesuar me faktin qe PHP-ja gjeneron nje gabim. Koncepti eshte i drejte, por lind pyetja se cdo te thote te gjenerohet nje gabim nga PHP-ja?

Ajo qe ne te vertete ndodh ne momentet kur hasemi ne situata qe ka gabim, eshte qe PHP-ja leshon ne sistem nje objekt i cili quhet Perjashtim (ang. Exception). Tipi i perjashtimit qe leshohet ne sistem varet nga lloji i gabimit. P.sh. ne rastin e gabimit te mesiperm, PHP-ja leshon ne sistem nje objekt te tipit InvalidArgumentException. E verteta eshte qe ekziston nje klase kryesore e brendshme e PHP-se e quajtur Exception. Kjo klase eshte klase prind per nje numer te madh klasash te tjerash sic jane InvalidArgumentException, OutOfBoundsException, RuntimeException, BadMethodCallException etj. te cilat varen nga lloji i gabimit. Nje klase e pergjithshme e cila i perfshin te gjitha llojet e perjashtimeve eshte klasa Exception. Kjo klase trashegohet nga shume nenklasa te cilat perfshijne gabime me specifikisht te ndare.

Kur ne ndonje pjese te kodit dicka shkon gabim, ne sistem leshohet nje perjashtim. Perjashtimet, ne momentin qe leshohen ne sistem, e ndalojne ekzekutimin e kodit ne ate pozicion qe u leshuan.

Disa lloje perjashtimesh, qe jane klase femije te superklases Exception:

  • BadFunctionCallException - Ne rast se therrasim nje funksion qe nuk ekziston, ose nuk kalojm numrin e duhur te argumenteve ne ndonje funksion atehere ne sistem leshohet nje perjashtim i ketij tipi.

  • BadMethodCallException - Ne rast se therrasim nje metode qe nuk ekziston te nje klase, ose nuk kalojm numrin e duhur te argumenteve ne ndonje metode atehere ne sistem leshohet nje perjashtim i ketij tipi.

  • InvalidArgumentException - Ne rast se ne nje funksion ose metode te caktuar, nuk kalojme tipin e duhur te argumentit sic e kerkon, atehere ne sistem leshohet nje perjashtim i ketij tipi.

  • OutOfRangeException - Ne rast se kerkojme te aksesojme nje indeks qe nuk ekziston p.sh. ne nje array, atehere ne sistem leshohet nje perjashtim i ketij tipi.

Klasa Exception

Se pse jemi te interesuar per metodat e klases Exception do ta kuptojme ne fund te ketij seksioni, por fillimisht le te sqarojme metodat kryesore te saj.

Konstruktori i klases Exception pranon argumentet e meposhtme:

<?php

function __construct($message = null, $code = 0, Exception $previous = null);
  • $message - Cdo objekt Exception qe leshohet ne sistem permban nje mesazh, i cili pershkruan gabimin qe ndodhi.
  • $code - Cdo objekt Exception qe leshohet ne sistem permban nje kod numerik per llojin e gabimit.
  • $previous - Koncept pak me i avancuar, por sherben per rastet kur kemi perjashtime te nderfutura. Jep perjashtimin e pare qe shkaktoi perjashtimin aktual.

Ne rastin kur kemi nje objekt perjashtimi, variablat e mesiperme nuk mund ti aksesojme drejtperdrejt pasi nuk jan public. Ajo cfare mund te aksesojme jane metodat getMessage() dhe getCode() te cilat na kthejne vleren e $message dhe $code respektivisht.

Si te leshojme nje perjashtim ne sistem

Parashtruam qe perjashtimet leshohen ne sistem nga vete ekzekutuesi i PHP-se, ose per lehtesi e themi nga vete PHP-ja. Perjashtime mund te leshojme edhe ne manualisht, ne kodin qe shkruajme. Perjashtimet leshohen duke perdorur termin throw. Mjafton qe kete term ta shoqerojme me nje objekt te tipit Exception, mund te jete edhe objekt i nje nenklase te saj.

Leshojme nje perjashtim ne nje skedar me emrin index.php:

<?php
throw new Exception("Ne sapo hodhem nje Exception");

Nese ekzekutojme kodin e mesiperm do te na renderizohet mesazhi:

PHP Fatal error:  Uncaught exception 'Exception' with message 'Ne sapo hodhem nje Exception' in /home/alban.afmeti/index.php:2
Stack trace:
#0 {main}
  thrown in /home/alban.afmeti/index.php on line 2

Nese jeni hasur me perpara me gabime qe gjeneron PHP-ja, pak a shume eshte e njejta paraqitje si ajo, por me mesazhe te ndryshme. Le te provojme n.q.s. hedhja e nje perjashtimi do t'a ndaloje ekzekutimin e kodit apo jo:

<?php

echo "Fillojme ekzekutimin e skriptit PHP...<br><br>";

throw new Exception("Ne sapo hodhem nje Exception"); 

echo "Ky rresht nuk duhet te printohet!!!";

Pas ekzekutimit, perftojme rezultatin:

Fillojme ekzekutimin e skriptit PHP...

PHP Fatal error:  Uncaught exception 'Exception' with message 'Ne sapo hodhem nje Exception' in /home/alban.afmeti/index.php:5
Stack trace:
#0 {main}
  thrown in /home/alban.afmeti/index.php on line 5

Duke pare rezultatin, dallojme qarte qe rreshti Ky rresht nuk duhet te printohet!!! nuk eshte printuar. Kjo verteton qe ne momentin e hedhjes se nje perjashtimi, ekzekutimi i skriptit nderpritet.

Perjashtime te pershtatura sipas nevojes

Ne mund te ndertojme vete nje tip perjashtimi, mjafton te trashegojme klasen baze Exception. Perjashtimin e perashtasim sipas nevojave tona duke i vendosur nje mesazh apo kod numerik qe deshirojme.

Supozojme qe kemi nje funksion voto(...) qe merr ne hyrje nje moshe, dhe pranon vetem moshat qe jane 18 vjec e lart. Ne rast te kundert funksioni do te leshoje nje perjashtim me nje mesazh te caktuar.

Ndertojme nje klase me emrin MosheEPalejuarException. Kur ndertojme klasa per perjashtime te pershtatur, gjithnje ne fund te emrit eshte praktike e mire qe te vendosim prapashtesen Exception per te dalluar qe klasa ne fjale eshte e trasheguar nga klasa Exception.

<?php

class MosheEPalejuarException extends Exception {

    function __construct() {
        parent::__construct("Mosha juaj nuk lejohet te votoje!!!", 1000);
    }
}

Brenda funksionit __construct, kemi therritur konstruktorin e klases prind me mesazhin Mosha juaj nuk lejohet te votoje!!!, dhe me nje kod numerik 1000.

Testojme perjashtimin e krijuar me ane te funksionit voto(...):

<?php

function voto(int $mosha) {

  if($mosha < 18) {

    throw new MosheEPalejuarException();

  } else {

    echo "Vota juaj kaloi me sukses!";

  }
}



voto(23);  // Rezultati: Vota juaj kaloi me sukses!

Ne rast se kalojme si argument nje numer me te madh se 17, do te printohet mesazhi Vota juaj kaloi me sukses!. Ne rast se kalojme nje numer me te vogel se 18 p.sh. voto(17) atehere do te leshohet perjashtimi dhe do te renderizohet mesazhi:

PHP Fatal error:  Uncaught exception 'MosheEPalejuarException' with message 'Mosha juaj nuk lejohet te votoje!!!' in /home/alban.afmeti/index.php:6
Stack trace:
#0 {main}
  thrown in /home/alban.afmeti/index.php on line 5

Supozojme qe kemi nje funksion connect() i cili ben lidhjen me bazen e te dhenave. Ne rast se lidhja nuk eshte e mundur te beher pet ndonje arsye, PHP-ja do te leshoje nje objekt te tipit Exception ne sistem. Supozojme qe objekti qe leshohet eshte i nenklases ConnectionException.

<?php

echo "Fillojme ekzekutimin e skriptit PHP...<br><br>";

connect();

echo "Rreshti i fundit!!!";

Pasi ndodh leshimi i perjashtimit, skripti nderpritet ne menyre te menjehershme dhe rreshti Rreshti i fundit!!! nuk printohet. Po sikur te duam qe ekzekutimi i skriptit te vazhdoje pavaresisht se ne sistem hidhet ky perjashtim? Kjo gje eshte e mundur nepermjet trajtimit te perjashtimeve.

Trajtimi i perjashtimeve

Trajtimi i perjashtimeve na vjen ne ndihme per menaxhuar perjashtimet e ndryshme qe hidhen ne sistem, qofshin ata te hedhur automatikisht nga PHP-ja ose te hedhur manualisht nga ne. Trajtimi i perjashtimeve behet nepermjet blloqeve try - catch (...).

Brenda bllokut try vendoset kodi qe duam te ekzekutohet dhe te menaxhohet njekohesisht duke pritur nga momenti ne moment per leshimin e ndonje perjashtimi. Blloku catch (...) brenda kllapave rrethore merr si parameter nje objekt perjashtimi, tipin e perjashtimit qe eshte i afte te trajtoje (te menaxhoje). P.sh. catch (Exception $ex) arrin te trajtoje te gjitha llojet e perjashtimeve sepse klasa Exception eshte klasa prind per te gjitha perjashtimet. Ndersa catch (MosheEPalejuarException $ex) arrin te trajtoje vetem perjashtime te tipit MosheEPalejuarException.

Marrim nje shembull sesi funksionon blloku try - catch.

<?php

echo "Fillojme ekzekutimin e skriptit PHP...<br><br>";

try {

  connect();

  echo "Ne rast perjashtimi ky rresht nuk ekzekutohet!";

} catch(Exception $ex) {

  echo "U hodh nje perjashtim! <br><br>";

}

echo "Rreshti i fundit!!!";

Brenda bllokut try kemi futur pjesen e kodit qe dyshohet te leshoje ndonje perjashtim. Ne rast se gjate ekzekutimit te funksionit connect() hidhet perjashtim nga PHP-ja, ekzekutimi i bllokut try nderpritet, dhe stafeta i kalon bllokut catch qe trajton perjashtimin e hedhur. Objekti perjashtim i hedhur nga PHP-ja, ne kete moment mund te aksesohet nepermjet references se parametrit te bllokut catch, pra referenca $ex. Me ane te kesaj reference mund te therrasim metoda te ndryshme te klases Exception sic eshte getMessage(), getCode() etj. Kur blloku catch merr stafeten, ekzekutohet i gjithe kodi brenda tij. Pasi perfundon ekzekutimi i bllokut catch, ekzekutimi vazhdon me pjesen tjeter te skriptit qe vijon.

Ne rast se ne kodin e mesiperm leshohet nje perjashtim, do te renderizohen mesazhet e meposhtme:

Fillojme ekzekutimin e skriptit PHP...

U hodh nje perjashtim!

Rreshti i fundit!!!

Ne rast se ne kodin e mesiperm nuk leshohet nje perjashtim, do te renderizohen mesazhet e meposhtme:

Fillojme ekzekutimin e skriptit PHP...

Ne rast perjashtimi ky rresht nuk ekzekutohet!

Rreshti i fundit!!!

Pra, dallojme qarte qe nese nuk kemi leshim perjashtimi, blloku catch injorohet.

Ne disa raste eshte i nevojshem me shume se nje bllok catch pasi nuk jemi te sigurte se cfare lloj perjashtimesh mund te hidhen ne sistem.

Shembull:

<?php

function voto(int $mosha) {

  if($mosha < 18) {

    throw new MosheEPalejuarException();

  } else {

    echo "Vota juaj kaloi me sukses!";

  }
}


try {

  voto(15);

} catch(MosheEPalejuarException $ex) {

  echo $ex->getMessage();  //Printojme mesazhin e vete perjashtimit

} catch(Exception $ex) {

  echo $ex->getMessage();  //Printojme mesazhin e vete perjashtimit

}

Ne ndryshim nga shembulli i pare, brenda bllokut catch printojme mesazhin e vete perjashtimit echo $ex->getMessage();. Ne rast se perjashtimi qe leshohet nga funksioni voto(...) eshte objekt i klases MosheEPalejuarException, atehere ai do te trajtohet nga blloku i pare catch. Ne te kundert, ne rast se perjashtimi qe leshohet eshte nje objekt i nje tipi tjeter, atehere ai trajtohet nga blloku i dyte catch. Arsyeja pse behet kjo gje eshte sepse blloku i dyte catch trajton perjashtimet e tipit Exception qe eshte nje klase prind per te gjitha klasat e perjashtimeve, ndaj eshte i afte te trajtoje te gjitha llojet e perjashtimeve.

Duhet te kemi kujdes, sepse ne momentin e leshimit te perjashtimit, blloqet catch skanohen me radhe nga i pari deri tek i fundit per te gjetur tipin qe trajton perjashtimin e hedhur.

Situata e meposhtme eshte gabim:

<?php

try {

  voto(15);

} catch(Exception $ex) {

  echo $ex->getMessage();

} catch(MosheEPalejuarException $ex) {

  echo $ex->getMessage();

}

Gabimi qendron ne faktin qe blloku i dyte catch nuk do te ekzekutohet asnjehere ne jete, pavaresisht llojit te perjashtimit qe hidhet. Edhe n.q.s. hidhet perjashtim i tipit MosheEPalejuarException, fillon skanimi per te gjetur bllokun catch qe eshte i afte te trajtoje kete perjashtim. Qe ne momentin e pare PHP-ja vlereson si te afte bllokun e pare catch sepse ai pranon objekte te klases Exception, e cila eshte klase prind per te gjitha llojet e perjashtimeve. Blloku i pare eshte i afte te trajtoje te gjitha llojet e perjashtimeve, per kete arsye blloqet e tjera catch edhe sikur te ishin me shumice nuk do te ekzekutoheshin kurre.

Ky problem zgjidhet duke vendosur gjithmone klasat femije ne fillim, dhe klasat prind ne fund. Pra, hierarkia e vendosjes se blloqeve catch duhet te nise nga femijet tek prinderit.

<?php

try {

  voto(15);

} catch(MosheEPalejuarException $ex) {

  echo $ex->getMessage();

} catch(Exception $ex) {

  echo $ex->getMessage();

}

Ne rastin e mesiperm, hedhja e nje perjashtimi te tipit MosheEPalejuarException do te trajtohet nga blloku i pare catch sepse ai eshte blloku i pare ne liste i afte per te trajtuar nje perjashtim te tille.

Blloqet finally

Blloqet finally jane vazhdim i blloqeve try - catch dhe vendosen ne fund te tyre.

<?php

try {

  voto(15);

} catch(MosheEPalejuarException $ex) {

  return 1;

}

echo "Rreshti i fundit!";

Ne shembullin e mesiperm, ne rast se leshohet nje perjashtim ekzekutohet blloku catch i cili thjesht kthen vleren 1 me ane te deklarates return 1;. Dukeqenese u perdor deklarata return kodi nuk vazhdon te ekzekutohet me poshte, pra teksti Rreshti i fundit! nuk printohet. Ne disa raste eshte i nevojshem ekzekutimi i nje pjese kodi, edhe pse brenda bllokut try - catch mund te kemi kthyer nje vlere me ane te deklarates return. Na vjen ne ndihme blloku finally:

<?php

try {

  voto(15);

} catch(MosheEPalejuarException $ex) {

  return 1;

} finally {

  echo "Blloku finally ekzekutohet gjithmone! <br><br>";
  echo "Rreshti i fundit!";
}

Kodi brenda bllokut finally ekzekutohet gjithmone pavaresisht se cfare ndodh ne blloqet try - catch. Ndaj, rezultati pas ekzekutimit te kodit te mesiperm do te jete:

Blloku finally ekzekutohet gjithmone!
Rreshti i fundit!

Zakonisht blloku finally perdoret per te mbyllur ndonje lidhje me databazen, me ndonje file te hapur etj. Vendoset brenda ketij blloku per te qene te sigurte qe lidhja do te mbyllet pavaresisht problemeve qe mund te lindin ne blloqet try - catch.

Rileshimi i perjashtimeve

Ndonjehere mund te jete i nevojshem rileshimi i perjashtimeve qe do te thote, ne e trajtojme nje perjashtim, shikojme pa informacion ne lidhje me te dhe e rihedhim perjashtimin perseri.

Shembull:

<?php

$filename = "tedhena.txt";

try {
    $text = file_get_contents($filename);  // Lexojme skedarin tedhena.txt

    if ($text === false) {  // Nqs. nuk arrijme te lexojme hedhim nje perjashtim
        throw new Exception();
    }

} catch (Exception $ex) {

    if (!file_exists($filename)) {  // Nqs. skedari nuk ekziston

        throw new FileExistException($filename . " nuk ekziston.");

    }

    elseif (!is_readable($filename)) {
        throw new FileReadException($filename . " nuk eshte i lexueshem.");  //Nqs. skedari eshte i palexueshem
    }

    else {
        throw new Exception("Gabim i panjohur per te aksesuar kete skedar.");
    }
}

Ne shembullin e mesiperm perpiqemi te lexojme skedarin tedhena.txt. Ne rast se leximi na kthen false hedhim nje perjashtim manualisht throw new Exception();. Ky perjashtim normalisht do te kapet dhe trajtohet nga blloku i pare catch. Behen disa kontrolle brenda bllokut catch. N.q.s. skedari nuk ekziston hidhet nje perjashtim i tipit FileExistException me nje mesazh "tedhena.txt nuk ekziston.". N.q.s. skedari eshte i palexueshem hidhet nje perjashtim i tipit FileReadException me nje mesazh "tedhena.txt nuk eshte i lexueshem.". Perndryshe hidhet nje perjashtim i tipit Exception me mesazhin "Gabim i panjohur per te aksesuar kete skedar.". Ne kete rast kemi bere nje rileshim te perjashtimit. Kur rileshojme nje perjashtim duhet te jemi te sigurte qe perjashtimi i hedhur do te trajtohet diku tjeter, ne menyre qe mos te perfundoje ekzekutimin e skriptit me gabim fatal.

results matching ""

    No results matching ""